

/**
 * @file BiasDataReader.cpp
 * Class to read Bias data.
 */

#include "BiasDataReader.hpp"
#include "StringUtils.hpp"


using namespace std;
using namespace gpstk::StringUtils;


namespace gpstk
{

    // Method to store load Bias data in this class' data map
    void BiasDataReader::loadData()
        noexcept(false)
    {

        try
        {
            satBiasData.clear();

            // a buffer
            string line;

            // read
            while( true )
            {
                formattedGetLine(line, true);

                if(line.substr(1,9) == "BIAS_MODE")
                {
                    mode = line.substr(41,8);
                }

                if(line.substr(1,4) == "DSB ")
                {
//                    mode = "RELATIVE";

                    string sys( line.substr(11,1) );

                    if( sys != "G" && sys != "R" &&
                        sys != "E" && sys != "C" )
                    {
                        continue;
                    }


                    string sat( line.substr(11, 3) );
                    string sta( line.substr(15, 9) );

                    // skip station bias
                    if( sta != string(9,' ') ) continue;

                    string type1( line.substr(25, 4) );
                    string type2( line.substr(30, 4) );

                    string time1( line.substr(35,14) );
                    string time2( line.substr(50,14) );

                    YDSTime ct1( asInt(time1.substr(0,4)),
                                 asInt(time1.substr(5,3)),
                                 asDouble(time1.substr(9,5)),
                                 TimeSystem::GPS );

                    YDSTime ct2( asInt(time2.substr(0,4)),
                                 asInt(time2.substr(5,3)),
                                 asDouble(time2.substr(9,5)),
                                 TimeSystem::GPS );

                    CommonTime start( ct1.convertToCommonTime() );

                    CommonTime end;

                    if(time2 == "0000:000:00000")
                    {
                        end = CommonTime::END_OF_TIME;
                    }
                    else
                    {
                        end = ct2.convertToCommonTime();
                    }

                    string index( sat + " " + type1 + type2 );

                    double value( asDouble(line.substr(70,21)) );
                    double std  ( asDouble(line.substr(92,11)) );

//                    cout << setw(15) << index
//                         << setw(30) << time1
//                         << setw(30) << time2
//                         << setw(10) << value
//                         << setw(10) << std
//                         << endl;

                    BiasData biasData;
                    biasData.start = start;
                    biasData.end   = end;
                    biasData.value = value;
                    biasData.std   = std;

                    satBiasData.insert( make_pair(index, biasData) );
                }
                else if(line.substr(1,4) == "OSB ")
                {
//                    mode = "ABSOLUTE";

                    string sys( line.substr(11,1) );

                    if( sys != "G" && sys != "R" &&
                        sys != "E" && sys != "C" )
                    {
                        continue;
                    }


                    string sat( line.substr(11, 3) );
                    string sta( line.substr(15, 9) );

                    // skip station bias
                    if( sta != string(9,' ') ) continue;

                    string type1( line.substr(25, 4) );
                    string type2( line.substr(30, 4) );

                    string time1( line.substr(35,14) );
                    string time2( line.substr(50,14) );

                    YDSTime ct1( asInt(time1.substr(0,4)),
                                 asInt(time1.substr(5,3)),
                                 asDouble(time1.substr(9,5)),
                                 TimeSystem::GPS );

                    YDSTime ct2( asInt(time2.substr(0,4)),
                                 asInt(time2.substr(5,3)),
                                 asDouble(time2.substr(9,5)),
                                 TimeSystem::GPS );

                    CommonTime start( ct1.convertToCommonTime() );

                    CommonTime end;

                    if(time2 == "0000:000:00000")
                    {
                        end = CommonTime::END_OF_TIME;
                    }
                    else
                    {
                        end = ct2.convertToCommonTime();
                    }

                    string index( sat + " " + type1 + type2 );

                    double value( asDouble(line.substr(70,21)) );
                    double std  ( asDouble(line.substr(92,11)) );

//                    cout << setw(15) << index
//                         << setw(30) << time1
//                         << setw(30) << time2
//                         << setw(10) << value
//                         << setw(10) << std
//                         << endl;

                    BiasData biasData;
                    biasData.start = start;
                    biasData.end   = end;
                    biasData.value = value;
                    biasData.std   = std;

                    satBiasData.insert( make_pair(index, biasData) );
                }

            } // End of "while( true )"

        }  // End of try block
        catch (EndOfFile& e)
        {
            // We should close this data stream before returning
            (*this).close();

            return;
        }
        catch (...)
        {
            // We should close this data stream before returning
            (*this).close();

            return;
        }

    }  // End of 'BiasDataReader::loadData()'


    // Method to open AND load Bias data file.
    void BiasDataReader::open(const char* fn)
    {
        // We need to be sure current data stream is closed
        (*this).close();

        // Open data stream
        FFTextStream::open(fn, std::ios::in);
        if( !FFTextStream::is_open() )
        {
            std::cerr << "The file " << fn << " doesn't exist!" << std::endl;
            exit(-1);
        }

        loadData();

        return;

    }  // End of method 'BiasDataReader::open()'


    // Method to open AND load Bias data file. It doesn't
    // clear data previously loaded.
    void BiasDataReader::open(const string& fn)
    {

        // We need to be sure current data stream is closed
        (*this).close();

        // Open data stream
        FFTextStream::open(fn.c_str(), std::ios::in);
        if( !FFTextStream::is_open() )
        {
            std::cerr << "The file " << fn << "doesn't exist!" << std::endl;
            exit(-1);
        }

        loadData();

        return;

    }  // End of method 'BiasDataReader::open()'


    /* Get satellite Bias.
     *
     * sat      SatID
     * type1    Type of Bias, e.g., "C1C" for DSB or OSB
     * type2    Type of Bias, e.g., "C1W" for DSB, "   " for OSB
     * time     Time
     *
     */
    double BiasDataReader::getSatBias( const SatID& sat,
                                       const std::string& type1,
                                       const std::string& type2,
                                       const CommonTime& time )
    {
        double ret(0.0);


        std::stringstream satstr;

        if( sat.system == SatelliteSystem::GPS )
        {
            satstr << "G";
        }
        else if( sat.system == SatelliteSystem::GLONASS )
        {
            satstr << "R";
        }
        else if( sat.system == SatelliteSystem::Galileo)
        {
            satstr << "E";
        }
        else if( sat.system == SatelliteSystem::BDS )
        {
            satstr << "C";
        }
        else
        {
            return ret;
        }

        if(sat.id < 10) satstr << "0";
        satstr << sat.id;


        if( mode == "RELATIVE" )
        {
            // index of multimap
            string index;

            index += satstr.str();
            index += string(1,' ');
            index += type1;
            index += string(1,' ');
            index += type2;
            index += string(1,' ');

            multimap<string,BiasData>::iterator beg( satBiasData.lower_bound(index) );
            multimap<string,BiasData>::iterator end( satBiasData.upper_bound(index) );

            for(multimap<string,BiasData>::iterator it = beg;
                it != end;
                ++it)
            {
                CommonTime start( it->second.start );
                CommonTime end  ( it->second.end   );

                if( time >= start && time < end )
                {
                    ret = it->second.value;
                }
            }
        }
        else if( mode == "ABSOLUTE" )
        {
            // bias of type1
            double value1(0.0);

            // index of multimap
            string index1;

            index1 += satstr.str();
            index1 += string(1,' ');
            index1 += type1;
            index1 += string(1,' ');
            index1 += string(3,' ');
            index1 += string(1,' ');

            multimap<string,BiasData>::iterator beg1( satBiasData.lower_bound(index1) );
            multimap<string,BiasData>::iterator end1( satBiasData.upper_bound(index1) );

            for(multimap<string,BiasData>::iterator it = beg1;
                it != end1;
                ++it)
            {
                CommonTime start( it->second.start );
                CommonTime end  ( it->second.end   );

                if( time >= start && time < end )
                {
                    value1 = it->second.value;
                }
            }

            // bias of type2
            double value2(0.0);

            string index2;

            index2 += satstr.str();
            index2 += string(1,' ');
            index2 += type2;
            index2 += string(1,' ');
            index2 += string(3,' ');
            index2 += string(1,' ');

            multimap<string,BiasData>::iterator beg2( satBiasData.lower_bound(index2) );
            multimap<string,BiasData>::iterator end2( satBiasData.upper_bound(index2) );

            for(multimap<string,BiasData>::iterator it = beg2;
                it != end2;
                ++it)
            {
                CommonTime start( it->second.start );
                CommonTime end  ( it->second.end   );

                if( time >= start && time < end )
                {
                    value2 = it->second.value;
                }

                ret = value1 - value2;
            }
        }

        return ret;

    } // End of 'double BiasDataReader::getSatBias(...)'


}  // End of namespace gpstk
